/*
 *  SoundElementFactory.h
 *  SoundController0002
 *
 *  Created by jiro on 10/06/29.
 *  Copyright 2010 jiro music. All rights reserved.
 *
 */

#pragma once

#include <set>
#include "SoundElement.h"
#include "WaveTable.h"
#include "WaveTableFactory.h"
#include "LineFactory.h"

namespace sc
{

	//	factory pattern
	template< class AudioData >
	class SoundElementFactory
	{
	private:

		static std::set< SoundElement< AudioData > > soundElements;

		typedef typename std::set< SoundElement< AudioData > >::iterator SoundElementIter;
		typedef std::pair< SoundElementIter, bool > IterBoolPair;

	public:

		static const SoundElement< AudioData >* Create( const WaveTable& soundTable, const WaveTable& dynamicsTable, double length )
		{
			IterBoolPair res = 
				soundElements.insert(
					SoundElement< AudioData >( soundTable, dynamicsTable, length ) );
			const SoundElement< AudioData >* element = &(*res.first);
			return element;
		}

		static const SoundElement< AudioData >* Create( 
			double attackEndTime, const Line* const attackDynamicsLine,
			double decayEndTime, const Line* const decayDynamicsLine,
			double sustainEndTime, const Line* const sustainDynamicsLine,
			double releaseEndTime, const Line* const releaseDynamicsLine,
			const WaveTable& waveTable )
		{			
			IterBoolPair res = 
				soundElements.insert(
					SoundElement< AudioData >(
						attackEndTime, attackDynamicsLine,
						decayEndTime, decayDynamicsLine,
						sustainEndTime, sustainDynamicsLine,
						releaseEndTime, releaseDynamicsLine,
						waveTable ) );
			return &(*res.first);
		}
		
		static const SoundElement< AudioData >* Create(
			bool setByRelatimeTime,
			double attackTime, const Line* const attackDynamicsLine,
			double decayTime, const Line* const decayDynamicsLine,
			double sustainTime, const Line* const sustainDynamicsLine,
			double releaseTime, const Line* const releaseDynamicsLine,
			const WaveTable& waveTable )
		{
			IterBoolPair res = 
				soundElements.insert(
					SoundElement< AudioData >(
						setByRelatimeTime,
						attackTime, attackDynamicsLine,
						decayTime, decayDynamicsLine,
						sustainTime, sustainDynamicsLine,
						releaseTime, releaseDynamicsLine,
						waveTable ) );
			return &(*res.first);
		}

		static const SoundElement< AudioData >* CreateWithDynamicsAdjuster( const SoundElement< AudioData >* const soundElement, double dynamicsAdjuster )
		{
			if ( soundElement->HasDynamicsTable() ) {

				const WaveTable& dynamicsTable = 
					WaveTableFactory::Create( 
						soundElement->GetDynamicsTable(),
						dynamicsAdjuster * soundElement->GetDynamicsTable().GetAdjuster() );
				return Create( soundElement->GetSoundTable(), dynamicsTable, soundElement->GetDuration() );
				
			} else {
				const Line* line;

				line = soundElement->GetAttackDynamicsLine();
				const Line* attackDynamicsLine = NULL;
				if ( line != NULL ) {
					attackDynamicsLine = & LineFactory::Create( 
						line->GetType(), 
						line->GetStartX(),
						dynamicsAdjuster * line->GetStartY(),
						line->GetEndX(),
						dynamicsAdjuster * line->GetEndY() );
				}

				line = soundElement->GetDecayDynamicsLine();
				const Line* decayDynamicsLine = NULL;
				if ( line != NULL ) {
					decayDynamicsLine = & LineFactory::Create( 
						line->GetType(),
						line->GetStartX(),
						dynamicsAdjuster * line->GetStartY(),
						line->GetEndX(),
						dynamicsAdjuster * line->GetEndY() );
				}
				
				line = soundElement->GetSustainDynamicsLine();
				const Line* sustainDynamicsLine = NULL;
				if ( line != NULL ) {
					sustainDynamicsLine = & LineFactory::Create( 
						line->GetType(), 
						line->GetStartX(),
						dynamicsAdjuster * line->GetStartY(),
						line->GetEndX(),
						dynamicsAdjuster * line->GetEndY() );
				}
				
				line = soundElement->GetReleaseDynamicsLine();
				const Line* releaseDynamicsLine = NULL;
				if ( line != NULL ) {
					releaseDynamicsLine = & LineFactory::Create( 
						line->GetType(), 
						line->GetStartX(),
						dynamicsAdjuster * line->GetStartY(),
						line->GetEndX(),
						dynamicsAdjuster * line->GetEndY() );
				}
				return Create(
					soundElement->GetAttackEndTime(),  attackDynamicsLine,
					soundElement->GetDecayEndTime(),   decayDynamicsLine,
					soundElement->GetSustainEndTime(), sustainDynamicsLine,
					soundElement->GetReleaseEndTime(), releaseDynamicsLine,
					soundElement->GetSoundTable() );
			}
		}
		
		static const SoundElement< AudioData >* CreateWithTimeAdjuter( const SoundElement< AudioData >* const soundElement, double timeAdjuster )
		{
			double attackEndTime = timeAdjuster * soundElement->GetAttackEndTime();
			double decayEndTime = timeAdjuster * soundElement->GetDecayEndTime();
			double sustainEndTime = timeAdjuster * soundElement->GetSustainEndTime();
			double releaseEndTime = timeAdjuster * soundElement->GetReleaseEndTime();
			return Create(
				attackEndTime, soundElement->GetAttackDynamicsLine(),
				decayEndTime, soundElement->GetDecayDynamicsLine(),
				sustainEndTime, soundElement->GetSustainDynamicsLine(),
				releaseEndTime, soundElement->GetReleaseDynamicsLine(),
				soundElement->GetWaveTable() );
		}

		static const SoundElement< AudioData >* CreateWithTime( const SoundElement< AudioData >* const soundElement, double time )
		{
			return CreateWithTimeAdjuter( soundElement, time * soundElement->GetDuration() );
		}

		static const SoundElement< AudioData >* CreateWithFrequency( const SoundElement< AudioData >* const soundElement, double frequency )
		{
			const WaveTable& waveTable = soundElement->GetSoundTable();
			const WaveTable& newWaveTable = WaveTableFactory::Create( waveTable.GetType(), frequency );
			return Create(
				soundElement->GetAttackEndTime(), soundElement->GetAttackDynamicsLine(),
				soundElement->GetDecayEndTime(), soundElement->GetDecayDynamicsLine(),
				soundElement->GetSustainEndTime(), soundElement->GetSustainDynamicsLine(),
				soundElement->GetReleaseEndTime(), soundElement->GetReleaseDynamicsLine(),
				newWaveTable );
		}

		static void DisposeInstances()
		{
			soundElements.clear();
		}
	};

	template< class AudioData >
	std::set< SoundElement< AudioData > > SoundElementFactory< AudioData >::soundElements;

	typedef SoundElementFactory< float > SoundElementFactory32;

	typedef SoundElementFactory< double > SoundElementFactory64;

}
